Functors and monads

Functors and monads are abstractions for sequencing computations that carry some extra structure — optionality (Maybe), multiplicity ([]), side effects (<Proc>), or discrete-event timing (Sequence). SCL supports these abstractions through type classes and dedicated block syntax.

Functor

A Functor is any type constructor f for which you can apply a function to the contained value without changing the structure:

fmap :: (a -> b) -> f a -> f b

Examples:

fmap (* 2) (Just 5)      // Just 10
fmap (* 2) Nothing       // Nothing
fmap (* 2) [1, 2, 3]     // [2, 4, 6]

Monad

A Monad extends Functor with two operations:

return :: a -> m a         // wrap a value
(>>=)  :: m a -> (a -> m b) -> m b   // bind / sequence

>>= (pronounced "bind") extracts the value from m a, passes it to the function, and returns the resulting m b. The monad laws ensure that sequencing is associative and that return acts as a neutral element.

The >> operator is >>= where the second argument ignores the result:

(>>) :: m a -> m b -> m b
ma >> mb = ma >>= \_ -> mb

The mdo block

mdo is syntactic sugar for a chain of >>= / >> calls. It is the standard way to sequence monadic operations in SCL:

liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
liftM2 f x y = mdo
    a <- x
    b <- y
    return (f a b)

Each a <- expr desugars to expr >>= \a -> .... A bare expression (without <-) desugars to expr >> ....

List as a monad

[] is a monad where >>= means "apply the function to every element and concatenate the results" (non-determinism / cartesian product):

> mdo
    x <- [1, 2, 3]
    y <- [10, 20]
    return (x + y)
[11, 21, 12, 22, 13, 23]

This is equivalent to a list comprehension [x + y | x <- [1,2,3], y <- [10,20]].

Maybe as a monad

Maybe is a monad where >>= short-circuits to Nothing as soon as any step fails. This eliminates nested match expressions when chaining fallible operations:

safeHead :: [a] -> Maybe a
safeHead []    = Nothing
safeHead (x:_) = Just x

safeTail :: [a] -> Maybe [a]
safeTail []     = Nothing
safeTail (_:xs) = Just xs

secondElement :: [a] -> Maybe a
secondElement xs = mdo
    rest <- safeTail xs
    safeHead rest

The edo block

edo is a variant of mdo for effectful (non-pure) monads such as <Proc> or <ReadGraph>. To use edo, the module header must declare the feature:

module {
    features = [edo]
}

With edo, monadic operations interleave with ordinary <Proc> effects:

readAndPrint :: String -> <Proc> ()
readAndPrint path = edo
    lines <- readLines path
    for lines print

Sequence monad

Sequence is the monad used for discrete-event simulation control (see 3.01 Simulation sequences). mdo is the recommended syntax for composing sequences:

waitAndAct :: Sequence ()
waitAndAct = mdo
    waitCondition (getVar "BUTTON#BINARY_VALUE")
    wait 5.0
    execute (setVar "VALVE#VA11_POSITION" 0.0)

Key monad combinators

Function Type Description
return a -> m a Wrap a pure value
>>= m a -> (a -> m b) -> m b Bind / sequence with result
>> m a -> m b -> m b Sequence, discard first result
fmap (a -> b) -> m a -> m b Map over the contained value
join m (m a) -> m a Flatten one layer of monad
sequence [m a] -> m [a] Run a list of actions, collect results
replicateM Integer -> m a -> m [a] Run an action N times, collect results
repeatForever m a -> m b Repeat an action indefinitely (sequences only)